
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)

--
-- Formspecs variables
-- 

local w = 12
local h = 11
local c = h-2
local m = 0.3
local step = 0.6
local privs_levels = admin_ui.privlevels

--
-- Formspecs helpers : scrollable container
--
function get_formspec_container(content, scrollmax)

 local container = ""
    .."scrollbaroptions[min=0;max="..tostring(scrollmax)
      ..";smallstep=10;largestep=100]"
    .."scrollbar["
      ..tostring(w-(1.8*m))..","..tostring(m+1)..";"
      ..tostring(m*1.3)..","..tostring(c-m)..";vertical;scrbar;1]"
    .."scroll_container["..tostring(m)..","..tostring(m+1)..";"
      ..tostring(w-(m*3))..","..tostring(c-m)..";scrbar;vertical;0.1]"
    ..content
    .."scroll_container_end[]"    
    .."button["..tostring(0.2)..","..tostring(c+1.1)..";2,0.8;save;Save]"    
    .."button["..tostring(2.2)..","..tostring(c+1.1)..";2,0.8;restore;Restore]"    
    .."button["..tostring(4.2)..","..tostring(c+1.1)..";2,0.8;ghost;Ghost]"    
    .."button["..tostring(6.2)..","..tostring(c+1.1)..";2,0.8;bring;Bring]"    
    .."button["..tostring(8.2)..","..tostring(c+1.1)..";2,0.8;kick;Kick]"    
    .."button_exit["..tostring(w - 1.2)..","..tostring(c+1.1)..";1,0.8;ok;Ok]"    
    .."button_exit["..tostring(w - 0.7)..","..tostring(0.2)..";0.6,0.6;exit;X]"    
    -- "button[<X>,<Y>;<W>,<H>;save;Save]" 

  return container 
end 

function formspec_content_areas_list(playername, X, Y)
  
  local content = ""
  if minetest.get_modpath('areas') and areas then
    local areaStrings = {}
    for id, area in pairs(areas.areas) do
      if areas:isAreaOwner(id, playername) then
          local label = ("[%s] %s"):format(
            minetest.colorize("#2b74ff",id),
            area.name)
          local tooltip = ("%s %s"):format(
            minetest.pos_to_string(area.pos1),
            minetest.pos_to_string(area.pos2))
          
          local children = areas:getChildren(id)
          if #children > 0 then
            tooltip = tooltip.."\n -> "..table.concat(children, ", ")
          end
        
        table.insert(areaStrings, {
            label = minetest.formspec_escape(label),
            tooltip = minetest.formspec_escape(tooltip)
            })
      end
    end

    Y = Y + step*2
    local areas_title = S("@1's protected areas",playername)..' :'
    if #areaStrings < 0 then
      areas_title = S("@1 doesn't own any protected areas")..' :'
    end
    content = content
      .. "label["..tostring(X)..","..tostring(Y)..";"..areas_title.."]"

    for _,str in ipairs(areaStrings) do
      Y = Y + step
      content = content
        .. "label["..tostring(X+(step))..","..tostring(Y).."; - "..str.label.."]"
        .. "tooltip["..tostring(X+(step))..","..tostring(Y)..";"
            ..w - (step/8)..","..step..";"..str.tooltip..";#778484;#000000]"

    end
  end
  
  return X, Y, content
end


--
-- Level helper
--
  function level_max(rolename)
  -- Returns the lower value between given level and the higher level available

    if not privs_levels[rolename] then return end
    local role = privs_levels[rolename]
    if not role.privs then return end
    
    -- if ( not level ) or ( level > #role.privs ) then 
      -- level = #role.privs end
    -- end
    
    return #role.privs
  end

  function level_privs(rolename, level, include_lower_levels, include_higher_levels)
    local role = privs_levels[rolename]
    local lmax = level_max(rolename)
    local level = tonumber(level)

    if lmax and level and level <= lmax then
      if ( level == 1 )
          or ( not include_lower_levels )
          or ( role and role.clear ) then
            return privs_levels[rolename]['privs'][level]
      else
        local privs = {}
        local lvmin, lvmax
        if include_lower_levels then
          lvmin = 1 ; lvmax = level
        elseif include_higher_levels then
          lvmin = level ; lvmax = lmax
        else
          lvmin = 1 ; lvmax = lmax
        end
        for i = lvmin,lvmax do
          for k, v in pairs(privs_levels[rolename]['privs'][i]) do
            privs[k] = v
          end
        end
        return privs
      end
    end
  end

  function level_privs_list(rolename, level, include_lower_levels)
    local privs = level_privs(rolename, level, include_lower_levels)
    if not privs then return end
    local list = {}
    for k, v in pairs(privs) do
      table.insert(list, k)
    end
    return list
  end


  function player_get_level(playername, rolename)
  -- Returns the level that player has in given role or nil

    local lmax = level_max(rolename)
    if not lmax then return end

    local player_level
    local role = privs_levels[rolename]      
    local has_lvl = true
    for l = 1,lmax do
      local lvl = role.privs[l]
      has_lvl = has_lvl and minetest.check_player_privs(playername, lvl)
      if has_lvl and role.clear then
        for k,v in pairs(minetest.get_player_privs(playername)) do
          if not lvl[k] then
            has_lvl = false
            break
          end
        end
      end
      if has_lvl then player_level = l
      else break end
    end
    return player_level, lmax
  end

  function player_has_level(playername, rolename, level)
    -- Test if player 
    local player_level, lmax = player_get_level(playername, rolename)
    
    return  (
              player_level 
              and player_level <= level 
              and level_max >= level
            )
  end
    
  function colorised_rolestr(rolename)
    if not privs_levels[rolename] then return end
    return minetest.colorize("#2b74ff", privs_levels[rolename].name)
  end
  function colorised_levelstr(level)
    -- return minetest.colorize("#99e6ff", S('level @1',tostring(level)))
    return minetest.colorize("#99e6ff", tostring(level))
  end

function colorised_rolechatstr(rolename, level, add)
  local add = add or 0
  -- local newlevel = level + add
  local lmax = level_max(rolename) 
  local rc = colorised_rolestr(rolename)
  if lmax > 1 then
    local lc = colorised_levelstr(level)
    if add < 0 then
      return S("You are not @1 at level @2 anymore", rc, lc)
    else
      return S("You are now @1 at level @2", rc, lc)
    end
  else
    if add < 0 then
      return S("You are not @1 anymore", rc)
    else
      return S("You are now @1", rc)
    end
  end
end

function colorised_rolelongstr(rolename, level, playername)
  local lmax = level_max(rolename) 
  local rc = colorised_rolestr(rolename)
  if lmax > 1 then
    local lc = colorised_levelstr(level)
    return S("@1 has role @2 at level @3", playername, rc, lc)
  else
    return S("@1 has role @2", playername, rc)      
  end
end

function colorised_roleshortstr(rolename, level, playername)
  local lmax = level_max(rolename) 
  local rc = colorised_rolestr(rolename)
  if lmax > 1 then
    local lc = colorised_levelstr(level)
    return S("@1 level @2", rc, lc)
  else
    return rc   
  end
end
  


--
-- Browse all privs lists : formspec
--
function formspec_privs(formspec, playername)
  
  local privs=minetest.get_player_privs(playername)
  local privlists=""
  local X = 0
  local Y = 0
  
  -- Privs that do not match anything registered
  -- for j,l in pairs(privs) do
    -- if not minetest.registered_privileges[j] then
      -- local selected = l
      -- if Y == 0 then Y = 0.4
      -- else Y = Y + step
      -- end
      -- privlists = privlists 
         -- .. "checkbox["..tostring(X)..","..tostring(Y)..";priv_"..j..";"
         -- .. minetest.colorize("#2b74ff",j)..";"..tostring(selected).."]"
      -- end
  -- end 
  
  for k, v in pairs(minetest.registered_privileges) do
    local selected = privs[k]
    if Y == 0 then Y = 0.4
    else Y = Y + step
    end
    
    privlists = privlists 
                .. "checkbox["..tostring(X)..","..tostring(Y)..";priv_"..k..";"
                   .. minetest.colorize("#2b74ff",k)..";"..tostring(selected).."]"
    privlists = privlists
                .. "label["..tostring(X+3)..","..tostring(Y)..";"..v.description.."]"
  end

  -- FIXME: how does this works ? no idea
  -- This gets close enough but makes no sense
  local scrollmax = ((Y * c) - h)

  formspec = formspec ..
    "label[0.2,0.5;"..S("Privs for @1 :", playername).."]"..
    get_formspec_container(privlists, scrollmax)

  return formspec 
end

--
-- Browse all privs lists : action on recieve fields
--
function on_receive_fields_privs(player, fields)
  if not fields.target then return end
  
  local target = fields.target
  if not minetest.player_exists(target) then return end
  local target_player = minetest.get_player_by_name(target)
  local prefix = 'priv_'
  local p = string.len(prefix)
  for k, v in pairs(fields) do
    if string.sub(k,1,p) == prefix then
      local priv = string.sub (k, p+1)
      -- minetest.set_player_privs(param, {})
      local privs=minetest.get_player_privs(target)

      if minetest.registered_privileges[priv] then
        if v == 'false' then
          privs[priv] = nil
          minetest.set_player_privs(target,privs)
          if target_player then
            minetest.chat_send_player(target, S("You lost the privilege '@1'", priv))
          end
        elseif v == 'true' then
          privs[priv] = true
          minetest.set_player_privs(target,privs)
          if target_player then
            minetest.chat_send_player(target, S("You have been granted the privilege '@1'", priv))
          end
        end
      end
    end
  end
end


--
-- Manage levels : formspec
--
function formspec_levels(formspec, playername)
  
  local privs=minetest.get_player_privs(playername)
  local content=""
  local X = 0
  local Y = 0
  

  -- if Y == 0 then Y = step/2 else Y = Y + step end
  -- local areas_title = S("@1's protected areas",playername)..' :'
  for k, v in pairs(privs_levels) do
    local player_level, lmax = player_get_level(playername, k) 
   
    Y = Y + (step/4)
    for i = 1,lmax do
      local selected = (player_level and player_level >= i)
      local rolestr = colorised_roleshortstr(k, i)    
      local tooltip = table.concat(level_privs_list(k,i),', ')
      Y = Y + step
      local levelid = "level_"..k.."_"..tostring(i)
      content = content 
      .. "checkbox["..tostring(X)..","..tostring(Y)
        ..";"..levelid..";"
        .. rolestr ..";"..tostring(selected).."]"
      .. "tooltip["..levelid..";"..tooltip..";#778484;#000000]"

    end
  end


  -- FIXME: how does this works ? no idea
  -- This gets close enough but makes no sense
  local scrollmax = ((Y * c) - h)

  formspec = formspec ..
    "label[0.2,0.5;"..S("Levels for @1 :", playername).."]"..
    get_formspec_container(content, scrollmax)

  return formspec 
end

--
-- Manage levels : action on recieve fields
--
function on_receive_fields_levels(player, fields)
  if not fields.target then return end
  
  local target = fields.target
  if not minetest.player_exists(target) then return end
  local target_player = minetest.get_player_by_name(target)
  local prefix = 'level_'
  local p = string.len(prefix)
  for k, v in pairs(fields) do
    if string.sub(k,1,p) == prefix then
      local rolename = string.sub (k, p+1, string.len(k)-2)
      local lvl = string.sub (k, string.len(k))
      
      local privs = minetest.get_player_privs(target)
      local role = privs_levels[rolename]
      if not role and role.privs then return end
      if v == 'true' then
        local l = 1
        if role.clear then
          local lvlprivs = level_privs(rolename, lvl)
          minetest.set_player_privs(target,lvlprivs)
        else
          local lvlprivs = level_privs(rolename, lvl, true)
          for priv,val in pairs(lvlprivs) do
              -- if minetest.registered_privileges[priv] then
                privs[priv] = true
              -- end
          end
          minetest.set_player_privs(target,privs)
        end
        if target_player then
          local msg = colorised_rolechatstr(rolename, lvl)
          minetest.chat_send_player(target, msg)
        end
      elseif v == 'false' then
        local lvlprivs
        if role.clear then
          lvlprivs = level_privs(rolename, lvl)
        end
        lvlprivs = level_privs(rolename, lvl, false, true)
        for priv,val in pairs(lvlprivs) do
          -- if minetest.registered_privileges[priv] then
            privs[priv] = nil
          -- end
        end
        minetest.set_player_privs(target,privs)
        if target_player then
          local msg = colorised_rolechatstr(rolename, lvl, -1)
          minetest.chat_send_player(target, msg)
        end
      end
    end
  end
  if fields.target and fields.current_tab then
    show_formspec(player:get_player_name(),
          fields.target, tonumber(fields.current_tab))
  end
end



--
-- Display infos : formspec
--
function formspec_infos(formspec, playername)
  
  local privs=minetest.get_player_privs(playername)
  local content=""
  local X = 0
  local Y = 0
  
  -- infos = infos
  -- infos = infos
      -- .."label["..tostring(X)..","..tostring(Y)..";"
      -- ..minetest.colorize("#2b74ff","LEVELS : ").."]"

  Y = Y + step/2
  local levels_title = S("@1's levels :",playername)
  -- content = content .. ("label[%d,%d;%s]"):format(X,Y,levels_title)
  content = content .. "label["..X..","..Y..";"..levels_title.."]"
  for k, v in pairs(privs_levels) do
    local player_level, lmax = player_get_level(playername, k) 
    if player_level and lmax then
    
      local rolestr = colorised_rolelongstr(k, player_level, playername)
      local tooltip = minetest.formspec_escape(
                        table.concat(
                            level_privs_list(k, player_level, true),
                            ', '))
      
      Y = Y + step
      content = content .. "label["..X..","..Y..";"..rolestr.."]"
        .. "tooltip["..X..","..Y..";"..(w-(m*3))..","..step..";"..tooltip..";#778484;#000000]"
        -- .. ("tooltip[%d,%d;%d,%d;%s;#778484;#000000]").format(X,Y,w-(m*3),step,tooltip)
        -- .. ("label[%d,%d;%s]").format(X,Y,rolestr)
    end
  end

  local X, Y, areas_list = formspec_content_areas_list(playername, X, Y)
  content = content .. areas_list
  -- FIXME: how does this works ? no idea
  -- This gets close enough but makes no sense
  local scrollmax = ((Y * c) - h)
  local status
  if minetest.get_player_by_name(playername) then 
       status = minetest.colorize("#99e6ff",S("online"))
  else status = minetest.colorize("#99e6ff",S("offline"))
  end
  
  formspec = formspec ..
    "label[0.2,0.5;"..S("Player '@1' is @2", playername, status).."]"..
    get_formspec_container(content, scrollmax)

  return formspec 
end





--


--
-- Menu tabs list
--
local tabs = {
  {
    name = "Infos",
    formspec = formspec_infos,
    -- on_receive_fields = on_receive_fields_infos
  },
  {
    name = "Levels",
    formspec = formspec_levels,
    on_receive_fields = on_receive_fields_levels
  },  
  {
    name = "Privs",
    formspec = formspec_privs,
    on_receive_fields = on_receive_fields_privs
  },
  {
    name = "Effects",
    -- formspec = formspec_effects,
    -- on_receive_fields = on_receive_fields_effects
  },
}

-- 
-- Menu builder
-- 
function get_tabheader(tab)
  local th
  th ="tabheader[0,0;1;tab;"
  for i = 1,#tabs do
    th = th.. tabs[i].name
    if i ~= #tabs then th = th.."," end
  end
  th = th..";"..tostring(tab) 
  th = th..";true;true]"
  return th
end


-- 
-- Formspec builder
-- 
function get_formspec(target, tab)

    -- minetest.set_player_privs(param, {})

    local formspec = "formspec_version[5]"
      .."size["..tostring(w)..","..tostring(h).."]"
      ..get_tabheader(tab) 
      .."field[-1,-1;0,0;target;;"..target.."]"
      .."field[-1,-2;0,0;current_tab;;"..tab.."]"
      
    formspec = tabs[tab].formspec(formspec, target)

    return formspec

end


-- 
-- Show formspec wrapper
-- 
function show_formspec(playername, target, tab)
  if not tab then tab = 1 end

  if minetest.get_player_by_name(playername) then
    if minetest.player_exists(target) then
      if tabs[tab].formspec then
        minetest.show_formspec(
              playername,
              modname..':form_'..tab,
              get_formspec(target, tab)
            )
      end
    else
      minetest.chat_send_player(playername, S("Player @1 does not exists on this server", target))
    end
  end
end
  

-- 
-- Chatcommands
-- 
minetest.register_chatcommand("privs_form", {
	params = "<playername>",
	description = S("list and describe privs"),
	privs = {},
	func = function(name, param)
    if param == "" then
      param = name
    end
    show_formspec(name, param)
  end
  })



-- 
-- Forms receive fields
-- 
minetest.register_on_player_receive_fields(function(player, formname, fields)
      for tab = 1, #tabs do
        if formname == modname..':form_'..tab then
          -- print(dump(fields))
          local player_name = player:get_player_name()
          if fields.target and fields.tab then
            show_formspec(player_name, fields.target, tonumber(fields.tab))
          elseif fields.quit then
            return
          elseif fields.target then
            if tabs[tab].on_receive_fields then
              tabs[tab].on_receive_fields(player, fields)
            end
          end
        end
      end
    end)
--]]



